#region Using References

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

#endregion

namespace Beaker.OpenCube.Metadata.TypeConverters
{
	public class KeyedMetadataDictionaryConverter<TKey, TMetadata, TKeyConverter>
		: KeyedMetadataDictionaryConverter<TKey, TMetadata>
		where TMetadata : IKeyedMetadata<TKey>
		where TKeyConverter : JsonConverter, new()
	{
		public KeyedMetadataDictionaryConverter()
			: base( typeof( TKeyConverter ) )
		{
		}
	}

	public class KeyedMetadataDictionaryConverter<TKey, TMetadata>
		: JsonConverter
		where TMetadata : IKeyedMetadata<TKey>
	{
		private readonly Type _keyConverterType;

		public KeyedMetadataDictionaryConverter()
			: this( null )
		{
		}

		public KeyedMetadataDictionaryConverter( Type keyConverterType )
		{
			_keyConverterType = keyConverterType;
		}

		/// <summary>
		/// Determines whether this instance can convert the specified object type.
		/// </summary>
		/// <param name="objectType">Type of the object.</param>
		/// <returns>
		/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
		/// </returns>
		public override bool CanConvert( Type objectType )
		{
			if( objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof( KeyedMetadataDictionary<,> ) )
				return true;

			return false;
		}

		/// <summary>
		/// Reads the JSON representation of the object.
		/// </summary>
		/// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param><param name="objectType">Type of the object.</param><param name="existingValue">The existing value of object being read.</param><param name="serializer">The calling serializer.</param>
		/// <returns>
		/// The object value.
		/// </returns>
		public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
		{
			var target = new KeyedMetadataDictionary<TKey, TMetadata>();

			JsonConverter converter = null;
			if( _keyConverterType != null )
				converter = (JsonConverter)Activator.CreateInstance( _keyConverterType );

			if( reader.TokenType != JsonToken.StartObject )
				throw new FormatException( "Expecting an object" );

			while( reader.Read() )
			{
				if( reader.TokenType == JsonToken.EndObject )
					break;

				if( reader.TokenType == JsonToken.PropertyName )
				{
					TKey key;

					if( converter != null )
						key = (TKey)converter.ReadJson( reader, typeof( TKey ), null, serializer );
					else
						key = (TKey)reader.Value;

					// get value
					if( reader.Read() )
					{
						TMetadata data = serializer.Deserialize<TMetadata>( reader );
						target.Add( key, data );
					}
				}
			}

			return target;
		}


		/// <summary>
		/// Writes the JSON representation of the object.
		/// </summary>
		/// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param><param name="value">The value.</param><param name="serializer">The calling serializer.</param>
		public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
		{
			throw new NotImplementedException();
		}

	}
}